﻿using System.Collections.Generic;
using UnityEngine;

namespace XFGameFramework.TweenSystem
{
    /// <summary>
    /// 缓动曲线业务逻辑
    /// </summary>
    public static class EaseManager
    {

        private delegate float EaseFuntion(float x);

        private readonly static Dictionary<Ease, EaseFuntion> functions = new Dictionary<Ease, EaseFuntion>
        {
            {Ease.InSine,easeInSine },
            {Ease.OutSine,easeOutSine },
            {Ease.InOutSine,easeInOutSine },
            {Ease.InQuad,easeInQuad },
            {Ease.OutQuad,easeOutQuad },
            {Ease.InOutQuad,easeInOutQuad },
            {Ease.InCubic,easeInCubic },
            {Ease.OutCubic,easeOutCubic },
            {Ease.InOutCubic,easeInOutCubic },
            {Ease.InQuart,easeInQuart },
            {Ease.OutQuart,easeOutQuart },
            {Ease.InOutQuart,easeInOutQuart },
            {Ease.InQuint,easeInQuint },
            {Ease.OutQuint,easeOutQuint },
            {Ease.InOutQuint,easeInOutQuint },
            {Ease.InExpo,easeInExpo },
            {Ease.OutExpo,easeOutExpo },
            {Ease.InOutExpo,easeInOutExpo },
            {Ease.InCirc,easeInCirc },
            {Ease.OutCirc,easeOutCirc },
            {Ease.InOutCirc,easeInOutCirc },
            {Ease.InBack,easeInBack },
            {Ease.OutBack,easeOutBack },
            {Ease.InOutBack,easeInOutBack },
            {Ease.InElastic,easeInElastic },
            {Ease.OutElastic,easeOutElastic },
            {Ease.InOutElastic,easeInOutElastic },
            {Ease.InBounce,easeInBounce },
            {Ease.OutBounce,easeOutBounce },
            {Ease.InOutBounce,easeInOutBounce },
        };

        /// <summary>
        /// 求缓动曲线的值
        /// </summary>
        /// <param name="ease">缓动曲线类型</param>
        /// <param name="x">x坐标</param>
        /// <returns>y坐标</returns>
        public static float Evaluate(Ease ease, float x)
        {
            x = Mathf.Clamp01(x);

            if (ease == Ease.Linear)
                return x;

            if(functions.ContainsKey(ease))
                return functions[ease](x);

            return 0;
        }
        
       

        private static float easeInSine(float x)
        {
            return 1 - Mathf.Cos((x * Mathf.PI) / 2);
        }

        private static float easeOutSine(float x)
        {
            return Mathf.Sin((x * Mathf.PI) / 2);
        }

        private static float easeInOutSine(float x)
        {
            return -(Mathf.Cos(Mathf.PI * x) - 1) / 2;
        }

        private static float easeInQuad(float x)
        {
            return x * x;
        }

        private static float easeOutQuad(float x)
        {
            return 1 - (1 - x) * (1 - x);
        }

        private static float easeInOutQuad(float x)
        {
            return x < 0.5 ? 2 * x * x : 1 - Mathf.Pow(-2 * x + 2, 2) / 2;
        }

        private static float easeInCubic(float x)
        {
            return x * x * x;
        }

        private static float easeOutCubic(float x)
        {
            return 1 - Mathf.Pow(1 - x, 3);
        }
        private static float easeInOutCubic(float x)
        {
            return x < 0.5 ? 4 * x * x * x : 1 - Mathf.Pow(-2 * x + 2, 3) / 2;
        }

        private static float easeInQuart(float x)
        {
            return x * x * x * x;
        }

        private static float easeOutQuart(float x)
        {
            return 1 - Mathf.Pow(1 - x, 4);
        }

        private static float easeInOutQuart(float x)
        {
            return x < 0.5 ? 8 * x * x * x * x : 1 - Mathf.Pow(-2 * x + 2, 4) / 2;
        }

        private static float easeInQuint(float x)
        {
            return x * x * x * x * x;
        }
        private static float easeOutQuint(float x)
        {
            return 1 - Mathf.Pow(1 - x, 5);
        }

        private static float easeInOutQuint(float x)
        {
            return x < 0.5 ? 16 * x * x * x * x * x : 1 - Mathf.Pow(-2 * x + 2, 5) / 2;
        }
         
        private static float easeInExpo(float x)
        {
            return x == 0 ? 0 : Mathf.Pow(2, 10 * x - 10);
        }

        private static float easeOutExpo(float x)
        {
            return x == 1 ? 1 : 1 - Mathf.Pow(2, -10 * x);
        }

        private static float easeInOutExpo(float x)
        {
            return x == 0 ? 0 : x == 1 ? 1 : x < 0.5 ? Mathf.Pow(2, 20 * x - 10) / 2 : (2 - Mathf.Pow(2, -20 * x + 10)) / 2;
        }


        private static float easeInCirc(float x)
        {
            return 1 - Mathf.Sqrt(1 - Mathf.Pow(x, 2));
        }

        private static float easeOutCirc(float x)
        {
            return Mathf.Sqrt(1 - Mathf.Pow(x - 1, 2));
        }

        private static float easeInOutCirc(float x)
        {
            return x < 0.5 ? (1 - Mathf.Sqrt(1 - Mathf.Pow(2 * x, 2))) / 2 : (Mathf.Sqrt(1 - Mathf.Pow(-2 * x + 2, 2)) + 1) / 2;
        }

        private static float easeInBack(float x)
        {
            return (1.70158f + 1) * x * x * x - 1.70158f * x * x;
        }

        private static float easeOutBack(float x)
        {
            return 1 + (1.70158f + 1) * Mathf.Pow(x - 1, 3) + 1.70158f * Mathf.Pow(x - 1, 2);
        }

        private static float easeInOutBack(float x)
        {
            float c1 = 1.70158f;
            float c2 = c1 * 1.525f;
            return x < 0.5 ? (Mathf.Pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2 : (Mathf.Pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
        }

        private static float easeInElastic(float x)
        {
            float c4 = (2 * Mathf.PI) / 3;
            return x == 0 ? 0 : x == 1 ? 1 : -Mathf.Pow(2, 10 * x - 10) * Mathf.Sin((x * 10 - 10.75f) * c4);
        }

        private static float easeOutElastic(float x)
        {
            float c4 = (2 * Mathf.PI) / 3;
            return x == 0 ? 0 : x == 1 ? 1 : Mathf.Pow(2, -10 * x) * Mathf.Sin((x * 10 - 0.75f) * c4) + 1;
        }

        private static float easeInOutElastic(float x)
        {
            float c5 = (2 * Mathf.PI) / 4.5f;
            return x == 0 ? 0 : x == 1 ? 1 : x < 0.5f ? -(Mathf.Pow(2, 20 * x - 10) * Mathf.Sin((20 * x - 11.125f) * c5)) / 2 : (Mathf.Pow(2, -20 * x + 10) * Mathf.Sin((20 * x - 11.125f) * c5)) / 2 + 1;
        }

        private static float easeInBounce(float x)
        {
            return 1 - easeOutBounce(1 - x);
        }

        private static float easeOutBounce(float x)
        {
            float n1 = 7.5625f;
            float d1 = 2.75f;

            if (x < 1 / d1)
            {
                return n1 * x * x;
            }
            else if (x < 2 / d1)
            {
                return n1 * (x -= 1.5f / d1) * x + 0.75f;
            }
            else if (x < 2.5 / d1)
            {
                return n1 * (x -= 2.25f / d1) * x + 0.9375f;
            }
            else
            {
                return n1 * (x -= 2.625f / d1) * x + 0.984375f;
            }
        }

        private static float easeInOutBounce(float x)
        {
            return x < 0.5   ? (1 - easeOutBounce(1 - 2 * x)) / 2 : (1 + easeOutBounce(2 * x - 1)) / 2;
        }  
    }

}

